#include "StdAfx.h"
#include "QuadTreeManager.h"
#include "QuadTreeNode.h"
#include "CoreEngine.h"

namespace FEngine
{

	QuadTreeManager::QuadTreeManager(float _size)
		: mMinimumSize(_size)
	{
		if (_size < 1.0f)
			mMinimumSize = 1.0f;
		
		mChildNode[0] = NULL;
		mChildNode[1] = NULL;
		mChildNode[2] = NULL;
		mChildNode[3] = NULL;

		mAABB = new AABB();
	}

	QuadTreeManager::~QuadTreeManager()
	{
		for (int i = 0; i < 4; ++i)
		{
			if (mChildNode[i])
				delete mChildNode[i];
		}
	}

	QuadTreeNode* QuadTreeManager::getNodeByPosition(const Vector2& _pos, QuadTreeNode* _nodeToCreate)
	{
		SquarePosition sqPos;
		
		if (_pos.x >= 0 && _pos.y >= 0)
			sqPos = SQUARE_RIGHT_TOP;
		else if (_pos.x >= 0 && _pos.y < 0)
			sqPos = SQUARE_RIGHT_BOTTOM;
		else if (_pos.x < 0 && _pos.y >= 0)
			sqPos = SQUARE_LEFT_TOP;
		else if (_pos.x < 0 && _pos.y < 0)
			sqPos = SQUARE_LEFT_BOTTOM;

		QuadTreeNode* parentNode = mChildNode[sqPos];
		// This is a special case where there is yet no added node
		if (parentNode == NULL)
		{
			// Get the node of the minimum size containing the position
			Vector3 parentPos(0, 0, 0);
			if (_pos.x >= 0)
				parentPos.x = (int)(_pos.x / mMinimumSize) * mMinimumSize;
			else
				parentPos.x = (int)((_pos.x / mMinimumSize) - 1) * mMinimumSize;
			if (_pos.y >= 0)
				parentPos.z = (int)(_pos.y / mMinimumSize) * mMinimumSize;
			else
				parentPos.z = (int)((_pos.y / mMinimumSize) - 1) * mMinimumSize;
			
			mChildNode[sqPos] = _nodeToCreate->_initialize(parentPos, mMinimumSize, 0, NULL);
			_calulateAABB();
			return mChildNode[sqPos];
		}

		// Check if the position is contained in the actual node
		QuadTreeNode* smallestNode = parentNode->addObject(_pos, _nodeToCreate);
		while (smallestNode == NULL)
		{
			Vector3 parentNodeCenter = parentNode->getCenterPosition();
			float newSize = parentNode->getSize() * 2;
			int newInversedDeep = parentNode->getInversedDeep() + 1;
				
			// Get the parent node of the minimum size containing the actual node
			Vector3 newParentPos(0, 0, 0);
			if (_pos.x >= 0)
				newParentPos.x = (int)(parentNodeCenter.x / newSize) * newSize;
			else
				newParentPos.x = (int)((parentNodeCenter.x / newSize) - 1) * newSize;
			if (_pos.y >= 0)
				newParentPos.z = (int)(parentNodeCenter.z / newSize) * newSize;
			else
				newParentPos.z = (int)((parentNodeCenter.z / newSize) - 1) * newSize;

			// Add the new parent node to the tree
			QuadTreeNode* newNode = new QuadTreeNode(newParentPos, newSize, newInversedDeep, NULL);
			newNode->_addChildNode(parentNode);

			// Check if the new node contains the position
			parentNode = newNode;
			smallestNode = parentNode->addObject(_pos, _nodeToCreate);
		}
		// Register the new parent node
		mChildNode[sqPos] = parentNode;
		_calulateAABB();

		return smallestNode;
	}
	
	std::list<QuadTreeNode*> QuadTreeManager::getChildNode() const
	{
		std::list<QuadTreeNode*> list;

		for (int i = 0; i < 4; ++i)
		{
			if (mChildNode[i])
				list.push_back(mChildNode[i]);
		}

		return list;
	}
	
	AABB* QuadTreeManager::getAABB() const
	{
		return mAABB;
	}
	
	float QuadTreeManager::getMinimumSize() const
	{
		return mMinimumSize;
	}
	
	void QuadTreeManager::_getListNodes()
	{
		mListNodes.clear();
		
		for (int i = 0; i < 4; ++i)
		{
			if (mChildNode[i])
				mChildNode[i]->_addToListIfFinal(&mListNodes);
		}
	}
	
	void QuadTreeManager::_calulateAABB()
	{
		if (mChildNode[0] == NULL && mChildNode[1] == NULL &&
			mChildNode[2] == NULL && mChildNode[3] == NULL)
		{
			mAABB->setMinPoint(Vector3(0, 0, 0));
			mAABB->setMaxPoint(Vector3(0, 0, 0));
			return;
		}

		float minX = FLT_MAX;
		float maxX = -FLT_MAX;
		float minY = FLT_MAX;
		float maxY = -FLT_MAX;
		float minZ = FLT_MAX;
		float maxZ = -FLT_MAX;

		for (int i = 0; i < 4; ++i)
		{
			if (mChildNode[i] == NULL)
				continue;

			Vector3 minPoint = mChildNode[i]->getAABB()->getMinPoint();
			Vector3 maxPoint = mChildNode[i]->getAABB()->getMaxPoint();

			if (minPoint.x < minX)
				minX = minPoint.x;
			if (maxPoint.x > maxX)
				maxX = maxPoint.x;
			if (minPoint.y < minY)
				minY = minPoint.y;
			if (maxPoint.y > maxY)
				maxY = maxPoint.y;
			if (minPoint.z < minZ)
				minZ = minPoint.z;
			if (maxPoint.z > maxZ)
				maxZ = maxPoint.z;
		}

		mAABB->setMinPoint(Vector3(minX, minY, minZ));
		mAABB->setMaxPoint(Vector3(maxX, maxY, maxZ));
	}

	void QuadTreeManager::checkVisibility(float _maxView)
	{
		mListVisibleNodes.clear();

		CoreEngine::getInstance()->getCamera()->_setCustomFrustumCulling(_maxView);

		for (int i = 0; i < 4; ++i)
		{
			if (mChildNode[i])
				mChildNode[i]->_computeFrustumCulling(&mListVisibleNodes, _maxView);
		}
	}

	std::list<QuadTreeNode*> QuadTreeManager::getNodes() const
	{
		return mListNodes;
	}

	std::list<QuadTreeNode*> QuadTreeManager::getVisibleNodes() const
	{
		return mListVisibleNodes;
	}
	
	int QuadTreeManager::getNumberNodes() const
	{
		return mListNodes.size();
	}
	
	int QuadTreeManager::getNumberVisibleNodes() const
	{
		return mListVisibleNodes.size();
	}

} // namespace FEngine